# 04 声明宏的基本结构与工作原理 声明宏(`macro_rules!`)本质上是一个**基于模式匹配的代码生成器**。它在编译器的语法分析阶段工作,核心流程为:**拿着宏调用代码去比对预设规则,匹配成功后将规则替换为对应的代码模板。** **基本语法格式:** ```text macro_rules! 宏名称 { (匹配模式) => { 生成的代码模板 }; } ``` 零参数宏示例: ```rust // 定义一个不接收任何参数的宏 macro_rules! say_hello { () => { println!("Hello, Rust macros!"); }; } fn main() { say_hello!(); // 调用时必须写括号 } ``` * **左侧 `()**`:表示匹配模式。这里是空括号,代表该宏**不接收任何参数**。 * **右侧 `{ ... }**`:表示匹配成功后直接拿来替换宏调用的**生成代码模板**。 --- ## 2. 宏如何匹配并生成代码?(执行过程) 宏的工作流程可以拆解为两个核心步骤:匹配(Matching)与 **展开(Expansion)**。 ### 示例:带参数的宏 ```rust macro_rules! create_function { ($func_name:ident) => { fn $func_name() { println!("你调用了函数: {}", stringify!($func_name)); } }; } // 调用 create_function!(say_hello); ``` ### 工作步骤 1. **匹配(Matching)**:编译器拿着传入的参数 `say_hello` 去对照模式 `($func_name:ident)`。 * `$func_name` 是宏变量,用来捕获传入的参数。 * `:ident` 是片段标识符,约束传入的必须是一个合法的标识符。 * 匹配成功,`$func_name` 被赋值为 `say_hello`。 1. **代码生成(Expansion / 替换)**:编译器把右侧模板中的 `$func_name` 整体替换为 `say_hello`,最终在编译前展开为标准 Rust 代码: ```rust fn say_hello() { println!("你调用了函数: {}", "say_hello"); } ``` --- ## 3. 宏的片段标识符(Fragment Specifiers)分类速查 在匹配模式中,你可以通过不同的标识符来约束传入参数的类型和结构: 1. **`:expr`(Expression)**:匹配任何合法的**表达式**(如 `2 + 2`、函数调用、字面量)。 2. **`:ident`(Identifier)**:匹配**标识符**(如变量名、函数名、结构体名如 `x`, `User`)。 3. **`:ty`(Type)**:匹配**数据类型**(见下文详解)。 4. **`:pat`(Pattern)**:匹配**模式**(如 `match` 或 `let` 中的 `Some(x)`、`1..=5`)。 5. **`:stmt`(Statement)**:匹配完整的**语句**(如 `let x = 5;`)。 6. **`:block`**:匹配**代码块**(由大括号包裹的一系列代码)。 7. **`:literal`**:匹配**字面量**(常数值,如 `42`、`"hello"`、`true`)。 8. **`:path`**:匹配**路径**(如 `std::collections::HashMap`)。 --- ## 4. 深入 `:ty`:支持内置与自定义类型 `:ty` 用于匹配类型,它不仅支持 Rust 官方的**内置类型**(如 `i32`, `bool`, `f64`),**完全也支持自定义类型**(如结构体、枚举、类型别名等)。 ### 自定义类型匹配示例 ```rust // 1. 定义自定义结构体 #[derive(Debug)] struct User { name: String, } // 2. 定义接收类型 ($t:ty) 和表达式 ($val:expr) 的宏 macro_rules! print_variable_info { ($t:ty, $val:expr) => { let x: $t = $val; println!("传入的值是: {:?}", x); }; } fn main() { // 3. 传入自定义结构体类型 User 以及其实例化表达式 print_variable_info!(User, User { name: "Alice".to_string() }); } ``` * **原因**:声明宏工作于词法/语法解析阶段,只要传入的名称符合大驼峰等语法规范,`:ty` 就会将其识别为合法的类型并完成代码填充。